package com.cdtye.shuoh.service.product.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cdtye.common.webcore.web.AjaxJson;
import com.cdtye.shuoh.domain.enums.DataTypeEnum;
import com.cdtye.shuoh.domain.product.entity.Product;
import com.cdtye.shuoh.domain.product.entity.ProductProperties;
import com.cdtye.shuoh.domain.product.entity.ProductServices;
import com.cdtye.shuoh.domain.product.entity.ProductTemplate;
import com.cdtye.shuoh.domain.product.vo.ProductVo;
import com.cdtye.shuoh.domain.tdengine.Fields;
import com.cdtye.shuoh.domain.tdengine.SuperTableDto;
import com.cdtye.shuoh.mapper.product.ProductMapper;
import com.cdtye.shuoh.service.product.ProductPropertiesService;
import com.cdtye.shuoh.service.product.ProductService;
import com.cdtye.shuoh.service.product.ProductServicesService;
import com.cdtye.shuoh.service.product.ProductTemplateService;
import com.cdtye.shuoh.service.tgengine.TdEngineService;
import com.cdtye.shuoh.util.text.CharsetKit;
import com.cdtye.shuoh.util.text.TdUtils;
import com.cdtye.shuoh.util.text.UUID;
import com.google.common.collect.Lists;
import com.jayway.jsonpath.JsonPath;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.cdtye.shuoh.domain.product.model.Properties;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {

    @Autowired
    private ProductServicesService productServicesService;

    @Autowired
    private ProductPropertiesService productPropertiesService;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private TdEngineService tdEngineService;

    @Autowired
    private ProductTemplateService productTemplateService;

    /**
     * 数据库名称
     */
    @Value("${spring.datasource.dynamic.datasource.tdengine.dbName:cms_shuoh}")
    private String dataBaseName;


    /**
     * 产品模型导入
     * @param file
     * @return
     */
    @Override
    public AjaxJson importProductJsonFile(MultipartFile file) throws Exception{
        // 获取文件名，带后缀
        String originalFilename = file.getOriginalFilename();
        // 获取文件的后缀格式
        String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
        // 校验json格式
        List<String> jsonType = Lists.newArrayList("json");
        StringBuilder sb = null;
        if (jsonType.contains(fileSuffix)) {
            //JSON格式
            try (InputStream inputStream = file.getInputStream()) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, CharsetKit.getFilecharset(file.getInputStream())));
                sb = new StringBuilder();
                String line = null;
                try {
                    while ((line = reader.readLine()) != null) {
                        sb.append(line);
                    }
                    //解析产品模型数据
                    return this.productJsonDataAnalysis(JSONObject.parseObject(sb.toString()));
                } catch (IOException e) {
                    log.error(e.getMessage());
                } finally {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
        }
        return AjaxJson.error("the file's suffix is illegal");
    }

    /**
     * 解析产品模型数据
     * @param content
     * @return
     */
    @Override
    public AjaxJson productJsonDataAnalysis(JSONObject content){
        try {
            //厂商ID
            String manufacturerId = JsonPath.read(content, "$.manufacturerId").toString();
            //厂商名称
            String manufacturerName = JsonPath.read(content, "$.manufacturerName").toString();
            //产品型号
            String model = JsonPath.read(content, "$.model").toString();
            //产品名称
            String productName = JsonPath.read(content, "$.productName").toString();
            //产品类型
            String productType = JsonPath.read(content, "$.productType").toString();

            //String productIdentification = JsonPath.read(content, "$.productIdentification").toString();
//            //产品序列号
//            String productSerial = JsonPath.read(content, "$.productSerial").toString();
            //数据格式
            String dataFormat = JsonPath.read(content, "$.dataFormat").toString();
            //设备类型
            String deviceType = JsonPath.read(content, "$.deviceType").toString();
            //设备接入平台的协议类型
            String protocolType = JsonPath.read(content, "$.protocolType").toString();
            //产品描述
            String remark = JsonPath.read(content, "$.remark").toString();
            //验证产品模型是否存在
            Product oneByManufacturerIdAndModelAndDeviceType = productMapper.findOneByManufacturerIdAndModelAndDeviceType(manufacturerId, model, deviceType);
            if (null != oneByManufacturerIdAndModelAndDeviceType) {
                return AjaxJson.error("产品模型已存在,无需上传");
            }
            //验证properties数据格式
            List readDatatype = JsonPath.read(content.toJSONString(), "$..properties[*].datatype");
            Map<String, Object> parsingErrorMessages = new HashMap<>();
            List<Object> list = new ArrayList<>();
            //验证datatype数据格式（int、decimal、string、float、binary、bool、timestamp、json）
            readDatatype.forEach(item -> {
                boolean flag = "int".equals(item) || "decimal".equals(item) || "string".equals(item) || "binary".equals(item) || "float".equals(item)
                        || "bool".equals(item) || "timestamp".equals(item) || "json".equals(item);
                list.add(flag);
                if (list.contains(false)) {
                    parsingErrorMessages.put("datatype:" + item, "Invalid product: Invalid dataType,must be one of [int、decimal、string、binary、bool、timestamp、json]");
                }
            });
            if (!parsingErrorMessages.isEmpty()) {
                return AjaxJson.error(parsingErrorMessages.toString());
            }
            //服务属性解析处理
            Product product = new Product();
            product.setProductName(productName);
            product.setProductIdentification(UUID.getUUID());
            product.setProductType(String.valueOf(productType));
            //product.setProductSerial(productSerial);
            product.setManufacturerId(manufacturerId);
            product.setManufacturerName(manufacturerName);
            product.setModel(model);
            product.setDataFormat(dataFormat);
            product.setDeviceType(deviceType);
            product.setProtocolType(protocolType);
            product.setRemark(remark);
            productMapper.insert(product);
            //添加服务数据
            JSONArray services = content.getJSONArray("services");
            for (int i = 0; i < services.size(); i++) {
                JSONObject service = services.getJSONObject(i);
                ProductServices productServices = new ProductServices();
                productServices.setServiceId(service.getString("serviceId"));
                productServices.setServiceName(service.getString("serviceId"));
                productServices.setProductIdentification(product.getProductIdentification());
                productServices.setDescription(service.getString("description"));
                productServicesService.insertServices(productServices);
                //添加属性数据
                JSONArray properties = service.getJSONArray("properties");
                for (int j = 0; j < properties.size(); j++) {
                    JSONObject propertie = properties.getJSONObject(j);
                    ProductProperties productProperties = new ProductProperties();
                    productProperties = JSONObject.parseObject(propertie.toJSONString(),ProductProperties.class);
                    productProperties.setServiceId(productServices.getId());
                    productPropertiesService.insertProductProperties(productProperties);
                }
            }
            //解析入库成功创建TD超级表及子表
            this.createSuperTable(product, services);
        } catch (Exception e) {
            log.error(e.getMessage());
            AjaxJson.error("操作失败");
        }
        return AjaxJson.success("操作成功");
    }


    /**
     * 根据产品模型创建超级表
     * @param product
     * @param services
     * @return
     * @throws Exception
     */
    @Override
    public AjaxJson createSuperTable(Product product, JSONArray services) throws Exception {
        //构建超级表入参对象
        SuperTableDto superTableDto = new SuperTableDto();
        try {
            loop:
            for (int i = 0; i < services.size(); i++) {
                JSONObject service = services.getJSONObject(i);
                //超级表名称命名规则:产品序类型_产品标识_服务名称
                String superTableName = TdUtils.getSuperTableName(product.getProductType(),product.getProductIdentification(),service.getString("serviceId"));
                //设置数据库名称和超级表名称
                superTableDto.setDataBaseName(dataBaseName);
                superTableDto.setSuperTableName(superTableName);
                //构建超级表的表结构字段列表
                JSONArray properties = service.getJSONArray("properties");
                //如果服务下属性值为空，没必要为该服务创建超级表，跳过该循环，进入下个服务
                if (properties.isEmpty()) {
                    continue;
                }
                //构建超级表的表结构字段列表
                List<Fields> schemaFields = new ArrayList<>();
                //超级表第一个字段数据类型必须为时间戳,默认Ts为当前系统时间
                Fields tsColumn = new Fields();
                tsColumn.setFieldName("ts");
                tsColumn.setDataType(DataTypeEnum.TIMESTAMP);
                schemaFields.add(tsColumn);
                //超级表第二个字段为事件发生时间数据类型必须为时间戳
                Fields eventTimeColumn = new Fields();
                eventTimeColumn.setFieldName("event_time");
                eventTimeColumn.setDataType(DataTypeEnum.TIMESTAMP);
                schemaFields.add(eventTimeColumn);
                //根据属性对象列表循环构建超级表表结构
                for (int j = 0; j < properties.size(); j++) {
                    JSONObject propertie = properties.getJSONObject(j);
                    //获取字段名称
                    String filedName = (String) propertie.get("name");
                    //获取该属性数据类型
                    String datatype = (String) propertie.get("datatype");
                    //获取该属性的数据大小
                    Integer size = (Integer) propertie.get("maxlength");
                    //获取该属性步长
                    float step = Float.valueOf(propertie.get("step").toString());
                    //添加超级表表结构字段
                    Fields fields = new Fields(filedName, datatype, size, step);
                    schemaFields.add(fields);
                }
                //构建超级表标签字段列表
                //根据业务逻辑，将超级表的标签字段定为
                // 1:设备标识：deviceIdentification
                List<Fields> tagsFields = new ArrayList<>();
                Fields tags = new Fields();
                tags.setFieldName("device_identification");
                tags.setDataType(DataTypeEnum.NCHAR);
                tags.setSize(64);
                tagsFields.add(tags);

                //设置超级表表结构列表
                superTableDto.setSchemaFields(schemaFields);
                //设置超级表标签字段列表
                superTableDto.setTagsFields(tagsFields);
                AjaxJson cstResult = tdEngineService.createSuperTable(superTableDto);
                //创建超级表报错，打印报错信息，并跳过该循环，继续为下个服务创建表
                if (200 != cstResult.getStatus()) {
                    log.error("Create SuperTable Exception: " + cstResult.getMessage());
                    continue loop;
                }

                //保存新的超级表对的表结构信息
                ProductTemplate productTemplate = productTemplateService.getByProductIdAndServiceId(product.getId(),service.getString("serviceId"));
                if(productTemplate == null) {
                    productTemplate = new ProductTemplate();
                }
                productTemplate.setProductId(product.getId());
                productTemplate.setProductIdentification(product.getProductIdentification());
                productTemplate.setTdengineJson(JSON.toJSONString(superTableDto));
                productTemplate.setServiceId(service.getString("serviceId"));
                productTemplateService.insertOrUpdateProductTemplate(productTemplate);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return AjaxJson.success("操作成功");
    }


    @Override
    public Product findOneByProductIdentificationAndProtocolType(String productIdentification, String protocolType) {
        return productMapper.findOneByProductIdentificationAndProtocolType(productIdentification, protocolType);
    }

    public Product findOneByModel(String model){
        QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.eq("MODEL",model);
        return productMapper.selectOne(wrapper);
    }

    public Product getByProductIdentification(String productIdentification){
        QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.eq("product_identification",productIdentification);
        return productMapper.selectOne(wrapper);
    }

    /**
     * 分页查询
     * @param vo
     * @return
     */
    public IPage<Product> pageList(ProductVo vo){
        return productMapper.pageList(vo.page(),vo);
    }

    /**
     * 获取详情
     * @param id
     * @return
     */
    public Product getById(String id){
        return productMapper.getById(id);
    }

    /**
     * 产品信息修改
     * @param vo
     * @return
     */
    public int updateProduct(ProductVo vo){
        return productMapper.updateProduct(vo);
    }

    /**
     * 获取列表
     * @param vo
     * @return
     */
    public List<Product> selectList(ProductVo vo){
        return productMapper.selectList(vo);
    }

    @Override
    public int deleteById(String id) {
        if(null!=id){
            Product product = getById(id);
            productServicesService.deleteByProductIdentification(product.getProductIdentification());
            productTemplateService.deleteByProductIdentification(product.getProductIdentification());
            productMapper.deleteById(id);
        }
        return 0;
    }

    @Override
    public Map<String, Object> stateStatistic() {
        return  productMapper.stateStatistic();
    }


}
